package com.yunlin.blecontroler;
/*
    ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
       Author   :  lixiaodaoaaa
       Date     :  2018/6/15
       Time     :  10:42
    ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 */

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.yunlin.bean.BleDevice;
import com.yunlin.bean.OpenLockStatus;
import com.yunlin.event.ReadDeviceInfoEvent;
import com.yunlin.protocal.BteErrorData;
import com.yunlin.protocal.BteProtocalCommandUtils;
import com.yunlin.utils.ConvertUtils;
import com.yunlin.utils.FormatUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import de.greenrobot.event.EventBus;

public class BleController{
    
    public final static int FUNCTION_OPEN_LOCK=12;
    public final static int FUNCTION_READ_BATTERY_INFO=13;
    public final static int FUNCTION_VERIFY_ADMIN=11;
    
    public int getCurrentFunctionMode(){
        return currentFunctionMode;
    }
    
    public void setCurrentFunctionMode(int currentFunctionMode){
        this.currentFunctionMode=currentFunctionMode;
    }
    
    private int currentFunctionMode=FUNCTION_OPEN_LOCK;
    
    
    public static int REQUEST_ENABLE_BLUETOOTH=102;
    private static String TAG=BleController.class.getSimpleName();
    private boolean isConnected=false;
    private boolean isScaning=false;
    private boolean isConnecting=false;
    private int failedReConnectTime=3;
    private int currentConnectTime=0;
    private int currentInitCharCount=0;
    private int maxInitRetryTime=3;
    private boolean isTimeOut=false;
    private String handShakeCommand="";
    private long previousTime=0l;
    
    
    private Context context;
    private BluetoothManager bluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothGatt mBluetoothGatt;
    private IBteScanConnectListner bleConnectListner;
    private List<BleDevice> listBteDevice;
    private BluetoothAdapter.LeScanCallback leScanCallBack;
    private ScanCallback btScanCallBack;
    
    
    private BleDevice lastConnectDevice;
    private ExecutorService executorService;
    private BleCharasticServices bleCharaService;
    
    private Runnable handshakeRunnable;
    private Runnable disconnectRunnable;
    private Handler handler;
    
    public BleController(Context context, IBteScanConnectListner bleConnectListner){
        bluetoothManager=(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter=bluetoothManager.getAdapter();
        this.bleConnectListner=bleConnectListner;
        this.context=context;
        handler=new Handler(Looper.getMainLooper());
    }
    
    
    public void init(){
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            Activity activity=(Activity) context;
            activity.startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH);
        }
        listBteDevice=new ArrayList<>();
        initBtScanllCallBack();
        currentConnectTime=0;
        executorService=Executors.newCachedThreadPool();
        
        
        handshakeRunnable=() -> {
            Log.i(TAG, "链接成功，执行握手");
            handshakeLock();
        };
        disconnectRunnable=() -> {
            Log.i(TAG, "连接超时 MainActivity  主动断开链接");
            setTimeOut(true);
            releaseConnect(true);
            bleConnectListner.onConnectTimeOut();
        };
    }
    
    
    private void initBtScanllCallBack(){
        btScanCallBack=new ScanCallback(){
            @Override
            public void onScanResult(int callbackType, ScanResult result){
                super.onScanResult(callbackType, result);
                if (listBteDevice == null) {
                    listBteDevice=new ArrayList<>();
                }
                BluetoothDevice device=result.getDevice();
                BleDevice bleDevice=new BleDevice(device, result);
                if (bleDevice.getBluetoothDevice().getName() == null) {
                    return;
                }
                if (BleDeviceUtils.checkDeviceIsLockDevice(device.getName())) {
                    
                    int bleDeviceIndex=BleDeviceUtils.checkBleDeviceInListBleIndex(bleDevice, listBteDevice);
                    
                    if (bleDeviceIndex < 0) {
                        listBteDevice.add(bleDevice);
                    }else {
                        listBteDevice.set(bleDeviceIndex, bleDevice);
                    }
                    bleConnectListner.onScanResult(listBteDevice);
                }
            }
        };
    }
    
    
    /**
     * mIsScanning=false;
     * mBleAdapter.stopLeScan(mBleScanCallback);
     */
    
    public void scanBteLockDevice(){
        handler.removeCallbacks(disconnectRunnable);
        Log.i(TAG, "start scan here ");
        
        if (isScaning) {
            Log.i(TAG, "is Scanning now  cannot rescanint");
            return;
        }
        
        if (listBteDevice == null) {
            listBteDevice=new ArrayList<>();
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            final List<ScanFilter> list=new ArrayList<>();
            ScanSettings build=new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
            mBluetoothAdapter.getBluetoothLeScanner().startScan(list, build, btScanCallBack);
        }else {
            mBluetoothAdapter.startLeScan(leScanCallBack);
        }
        isScaning=true;
    }
    
    
    public void stopScan(){
        Log.i(TAG, "stop scan here ");
        if (isScaning) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mBluetoothAdapter.getBluetoothLeScanner().flushPendingScanResults(btScanCallBack);
                mBluetoothAdapter.getBluetoothLeScanner().stopScan(btScanCallBack);
            }else {
                mBluetoothAdapter.stopLeScan(leScanCallBack);
            }
        }
        isScaning=false;
    }
    
    
    public void connectDevice(BleDevice bleDevice, boolean reConnect){
        boolean connectResult=true;
        stopScan();
        previousTime=System.currentTimeMillis();
        
        if (isConnecting) {
            Log.i(TAG, "other device is connecting  not allowed you connect it ");
            bleConnectListner.onOtheDeviceIsConnecting();
            connectResult = false;
        }
        
        if (lastConnectDevice != null && lastConnectDevice.equals(bleDevice) && !reConnect) {
            bleConnectListner.onBleBusy();
            Log.i(TAG, " 蓝牙准备中 不允许连接哦");
            connectResult = false;
        }
        
        if (!connectResult) {
            scanBteLockDevice();
            return;
        }
        
        handler.postDelayed(disconnectRunnable, 11 * 1000);
        this.lastConnectDevice=bleDevice;
        isScaning=false;
        try {
            mBluetoothGatt=executorService.submit(new ConnectThread(context, bleDevice.getBluetoothDevice(), gattCallback)).get();
            Log.i(TAG, "开始连接设备：" + mBluetoothGatt.getDevice().getName());
            bleCharaService=new BleCharasticServices(mBluetoothGatt, new BleCharaInitListner(){
                @Override
                public void onInitSuccess(){
                    bleConnectListner.onBleConnect();
                    handler.postDelayed(handshakeRunnable, 300);
                }
                
                @Override
                public void onInitError(){
                    Log.i(TAG, " bleCharaService init failed  again  bleCharaService.initServiceAndChara() ");
                    if (currentInitCharCount < maxInitRetryTime) {
                        currentInitCharCount++;
                        bleCharaService.initServiceAndChara();
                    }else {
                        if (lastConnectDevice != null && currentConnectTime < failedReConnectTime) {
                            isConnecting=false;
                            if (isTimeOut) {
                                Log.i(TAG, " 超时 不能再次连接了 ");
                                releaseConnect(true);
                                return;
                            }
                            connectDevice(lastConnectDevice, true);
                            currentConnectTime++;
                            Log.i(TAG, " 连接失败，重新连接 ");
                        }else {
                            releaseConnect(true);
                            Log.i(TAG, " 超过最大重连次数 释放连接 ");
                        }
                    }
                }
            });
            handShakeCommand=BteProtocalCommandUtils.getHandShakeCommnd(bleDevice);
            isConnecting=true;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private BluetoothGattCallback gattCallback=new BluetoothGattCallback(){
        /**
         * 断开或连接 状态发生变化时调用
         * */
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){
            Log.e(TAG, "onConnectionStateChange()" + status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                isConnected=true;
                //连接成功
                if (newState == BluetoothGatt.STATE_CONNECTED) {
                    Log.e(TAG, "连接成功");
                    //发现服务
                    if (gatt != null) {
                        gatt.discoverServices();
                    }
                }
            }else {
                isConnected=false;
                bleConnectListner.onBleDisConnect();
                handler.removeCallbacks(handshakeRunnable);
                //连接失败
                Log.e(TAG, "连接失败");
                Log.e(TAG, "失败==" + status);
                
                //连接失败重新链接
                if (status == BteErrorData.NEED_RECONNECT_CODE || status == 62) {
                    if (lastConnectDevice != null && currentConnectTime < failedReConnectTime) {
                        isConnecting=false;
                        if (isTimeOut) {
                            Log.i(TAG, " 超时 不能再次连接了 ");
                            releaseConnect(true);
                            return;
                        }
                        connectDevice(lastConnectDevice, true);
                        currentConnectTime++;
                        Log.i(TAG, " 连接失败，重新连接 ");
                    }else {
                        releaseConnect(true);
                        Log.i(TAG, " 超过最大重连次数 释放连接 ");
                    }
                    
                }
                
                if (gatt != null) {
                    gatt.disconnect();
                    isConnected=false;
                }
            }
        }
        
        /**
         * 发现设备（真正建立连接）
         * */
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status){
            super.onServicesDiscovered(gatt, status);
            //直到这里才是真正建立了可通信的连接
            Log.e(TAG, "onServicesDiscovered()---建立连接");
            //获取初始化服务和特征值
            
            Thread initBleServiceThread=new Thread(() -> {
                if (null != bleCharaService) {
                    bleCharaService.initServiceAndChara();
                }
            });
            initBleServiceThread.start();
            try {
                initBleServiceThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            //订阅通知
            if (mBluetoothGatt == null) {
                releaseConnect(true);
                return;
            }
            isConnected=true;
        }
        
        
        /**
         * 写操作的回调
         * */
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){
            super.onCharacteristicWrite(gatt, characteristic, status);
            
            Log.e(TAG, "写操作的回调 onCharacteristicWrite()  status=" + status + ",value=" + ConvertUtils.bytesToStringData(characteristic.getValue()));
        }
        
        /**
         * 接收到硬件返回的数据
         * */
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic){
            
            if (characteristic == null || gatt == null) {
                Log.i(TAG, "characteristic is null   ");
                return;
            }
            
            String readDataStr=FormatUtils.bytesToHexString(characteristic.getValue());
            
            Log.i(TAG, "readDataStr is " + readDataStr);
            
            //"ee" data error
            if (ReadDataUtils.checkSendDataFormatterIsError(readDataStr)) {
                Log.i(TAG, "数据出错，断开链接 请重新扫描 ");
                bleConnectListner.needReScan();
                releaseConnect(true);
                return;
            }
            
            String rawData=BteProtocalCommandUtils.decodeReiceveData(lastConnectDevice, readDataStr);
            
            Log.i(TAG, "rawData  decode here is " + rawData);
            if (ReadDataUtils.isReadDeviceInfo(rawData)) {
                ReadDeviceInfoEvent deviceInfoEvent=ReadDataUtils.readDeviceInfo(rawData);
                EventBus.getDefault().post(deviceInfoEvent);
                return;
            }
            
            if (ReadDataUtils.isNeedOpenLock(rawData)) {
                switch (currentFunctionMode) {
                    case FUNCTION_OPEN_LOCK:
                        openLock();
                        break;
                    case FUNCTION_READ_BATTERY_INFO:
                        readBatteryInfo();
                        break;
                    case FUNCTION_VERIFY_ADMIN:
                        verifyAdmin();
                        break;
                    default:
                        openLock();
                        break;
                }
                return;
            }
            
            int userTime=Integer.valueOf((System.currentTimeMillis() - previousTime) / 1000 + "");
            OpenLockStatus openLockStatus=ReadDataUtils.readDataToOpenLockStatus(rawData, userTime);
            Log.i(TAG, "rawData is " + rawData);
            if (openLockStatus.isOpenSuccess()) {
                Log.i(TAG, "开锁成功");
                releaseConnect(true);
                handler.removeCallbacks(disconnectRunnable);
            }else {
                Log.i(TAG, "开锁失败 重新开锁");
                openLock();
            }
            bleConnectListner.onReiceveBleDeviceDat(openLockStatus);
            Log.i(TAG, "rawData is " + rawData);
        }
    };
    
    
    public void handshakeLock(){
        writeData(handShakeCommand);
    }
    
    
    public void writeData(String writeData){
        if (!isConnected) {
            Log.i(TAG, " 未连接成功握手 蓝牙设备 不能发送数据");
            return;
        }
        bleCharaService.writeData(writeData);
        bleCharaService.readData();
    }
    
    
    public boolean isScaning(){
        return isScaning;
    }
    
    public void setIsScaning(boolean isScaning){
        this.isScaning=isScaning;
    }
    
    public void releaseConnect(boolean isNeedReScan){
        bleConnectListner.onBleDisConnect();
        handler.removeCallbacks(handshakeRunnable);
        try {
            if (mBluetoothGatt != null) {
                mBluetoothGatt.disconnect();
                if (mBluetoothGatt != null) {
                    mBluetoothGatt.close();
                }
                mBluetoothGatt=null;
                listBteDevice=null;
            }
            Log.i(TAG, " is destory  the  BleController  ");
        } catch (Exception e) {
            e.printStackTrace();
            Log.i(TAG, "destory the ble error  msg is " + e.getMessage());
        }
        if (isNeedReScan || isTimeOut) {
            scanBteLockDevice();
        }
        isTimeOut=false;
        isConnecting=false;
        isConnected=false;
        currentConnectTime=0;
    }
    
    public void destoryBle(){
        try {
            releaseConnect(false);
            executorService.shutdownNow();
            
        } catch (Exception e) {
            e.printStackTrace();
            Log.i(TAG, "destory the ble error  msg is " + e.getMessage());
        }
    }
    
    public void openLock(){
        Log.i(TAG, "openLock here");
        String openLockCommand=BteProtocalCommandUtils.getOpenLockCommand(lastConnectDevice, "123456");
        writeData(openLockCommand);
    }
    
    public void verifyAdmin(){
        Log.i(TAG, "verify  here");
        String verifyAdmin=BteProtocalCommandUtils.getAdminVerify(lastConnectDevice, "123456");
        writeData(verifyAdmin);
    }
    
    public void readBatteryInfo(){
        Log.i(TAG, "readBatteryInfo here");
        String verifyAdmin=BteProtocalCommandUtils.getDeviceInfo(lastConnectDevice);
        writeData(verifyAdmin);
    }
    
    public boolean isTimeOut(){
        return isTimeOut;
    }
    
    public void setTimeOut(boolean timeOut){
        this.isTimeOut=timeOut;
    }
    
    
}
